package com.noob.assessment.web.controller;

import com.noob.assessment.api.ApiResponse;
import com.noob.assessment.domain.bo.FileUpdateBO;
import com.noob.assessment.domain.dto.FileUpdateResultDTO;
import com.noob.assessment.domain.dto.LockResultDTO;
import com.noob.assessment.domain.po.FileInfoPO;
import com.noob.assessment.domain.vo.*;
import com.noob.assessment.service.FileInfoService;
import com.noob.assessment.service.FileLockService;
import com.noob.assessment.service.FileService;
import com.noob.assessment.utils.BeanCopyUtil;
import com.noob.assessment.web.interceptor.UserContext;
import lombok.extern.java.Log;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;

/**
 * @author: zhengwq
 * datetime: 2020-04-16 23:31
 * description: 文件控制器
 */
@Log
@CrossOrigin
@Controller
@RequestMapping("/api/file")
public class FileController {

    /**
     * 文件信息服务
     */
    @Autowired
    private FileInfoService fileInfoService;

    /**
     * 文件锁服务
     */
    @Autowired
    private FileLockService fileLockService;

    /**
     * 文件服务
     */
    @Autowired
    private FileService fileService;

    /**
     * 文件的本地存储目录
     */
    @Value("${storage.folder}")
    private String storageFolder;

    /**
     * 创建文件
     *
     * @param fileCreateVO 文件创建vo
     * @return 操作结果 成功 true 失败：false
     */
    @PostMapping("/create")
    @ResponseBody
    public ApiResponse<Boolean> createFile(@RequestBody FileCreateVO fileCreateVO) {
        // 执行创建文件
        boolean result = fileService.createFile(fileCreateVO.getFileName(), fileCreateVO.getFileContent());

        return ApiResponse.ok(result);
    }

    /**
     * 查看所有文件
     *
     * @return 文件列表
     */
    @GetMapping("/list")
    @ResponseBody
    public ApiResponse<List<FileListItemVO>> getFileList() {

        // 查询文件列表
        List<FileInfoPO> fileInfoPos = fileInfoService.list();

        // 包装vo
        List<FileListItemVO> fileListItemVOS = BeanCopyUtil.copyListProperties(fileInfoPos, FileListItemVO::new);

        return ApiResponse.ok(fileListItemVOS);
    }

    /**
     * 查看指定文件
     *
     * @param fileId 文件id
     * @return 文件vo
     */
    @GetMapping("/get/{fileId}")
    @ResponseBody
    public ApiResponse<FileVO> getFileById(@PathVariable("fileId") int fileId) {

        // 根据文件id查询文件
        FileInfoPO fileInfoPO = fileInfoService.getById(fileId);
        String fileContent = fileService.getFileContent(fileInfoPO.getFileName());

        // 包装vo
        FileVO fileVO = new FileVO();
        BeanUtils.copyProperties(fileInfoPO, fileVO);
        fileVO.setFileContent(fileContent);

        return ApiResponse.ok(fileVO);
    }

    /**
     * 更新文件内容
     * 抢到文件锁就可以更新
     *
     * @param fileUpdateVO 文件更新vo
     * @return 文件更新结果vo
     */
    @PostMapping("/update")
    @ResponseBody
    public ApiResponse<FileUpdateResultVO> updateFile(@RequestBody FileUpdateVO fileUpdateVO) {

        // 获取用户id
        String userId = UserContext.getUserId();
        // 包装 文件更新bo
        FileUpdateBO fileUpdateBO = new FileUpdateBO();
        BeanUtils.copyProperties(fileUpdateVO, fileUpdateBO);
        fileUpdateBO.setUserId(userId);

        //执行文件更新
        FileUpdateResultDTO fileUpdateResultDTO = fileService.updateFile(fileUpdateBO);

        //包装vo
        FileUpdateResultVO fileUpdateResultVO = new FileUpdateResultVO();
        BeanUtils.copyProperties(fileUpdateResultDTO, fileUpdateResultVO);

        return ApiResponse.ok(fileUpdateResultVO);
    }

    /**
     * 本接口有两个调用时机：
     * 1. 进入edit页面前 需要获取文件锁才允许进入
     * 获取锁成功：进入edit页面 并可以正常编辑文件60s
     * 获取锁失败：没有抢到文件锁时提示holder和ttl
     * 2. 用户可能直接不通过view页面进入edit页面 而是直接访问edit页面的url链接 这种情况下也需要先获取锁
     * 虽然通过url方式进入edit页面时是可以先不获取文件锁 但是当用户编辑完更新内容后再提示文件被锁定 通常是让人沮丧的
     * 获取锁成功：可以正常编辑文件内容60s
     * 获取锁失败：等待ttl时长后 重新发起抢锁 但是查看文件内容是被允许的
     * 重新发起抢锁: 即重新刷新edit页面  或者 在view页面点击文件名右边的edit按钮进入edit页面
     *
     * @param fileId
     * @return
     */
    @GetMapping("/tryLock/{fileId}")
    @ResponseBody
    public ApiResponse<LockResultVO> tryLockFile(@PathVariable("fileId") int fileId) {

        // 获取用户id
        String userId = UserContext.getUserId();

        // 执行文件加锁
        LockResultDTO lockResultDTO = fileLockService.tryLockFile(fileId, userId);

        // 包装vo
        LockResultVO lockResultVO = new LockResultVO();
        BeanUtils.copyProperties(lockResultDTO, lockResultVO);

        return ApiResponse.ok(lockResultVO);
    }

    /**
     * 文件下载
     *
     * @param fileName 文件名
     * @param response 文件二进制流
     * @return
     */
    @GetMapping(value = "/download")
    public void download(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {

        // 获取文件全路径
        String fileFullPath = storageFolder + fileName;
        File file = new File(fileFullPath);

        // 设置response的Header
        response.setHeader("Content-Disposition", "attachment;filename="
                + new String(fileName.getBytes(), "iso-8859-1"));
        response.addHeader("Content-Length", "" + file.length());
        response.setContentType("application/octet-stream");

        // io流拷贝
        try (
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())
        ) {
            IOUtils.copy(bis, bos);
        } catch (IOException ex) {
            log.severe("下载文件异常！");
            ex.printStackTrace();
        }
    }

}
